// 
//  Author:
//    Matt Cooper vtbassmatt@gmail.com
// 
//  Copyright (c) 2010, Matt Cooper
// 
//  Microsoft Public License (Ms-PL)
// 
// This license governs use of the accompanying software. If you use the
// software, you accept this license. If you do not accept the license, do not
// use the software.
// 
// 1. Definitions
// 
// The terms "reproduce," "reproduction," "derivative works," and
// "distribution" have the same meaning here as under U.S. copyright law.
// 
// A "contribution" is the original software, or any additions or changes to
// the software.
// 
// A "contributor" is any person that distributes its contribution under this
// license.
// 
// "Licensed patents" are a contributor's patent claims that read directly on
// its contribution.
// 
// 2. Grant of Rights
// 
// (A) Copyright Grant- Subject to the terms of this license, including the
// license conditions and limitations in section 3, each contributor grants
// you a non-exclusive, worldwide, royalty-free copyright license to
// reproduce its contribution, prepare derivative works of its contribution,
// and distribute its contribution or any derivative works that you create.
// 
// (B) Patent Grant- Subject to the terms of this license, including the
// license conditions and limitations in section 3, each contributor grants
// you a non-exclusive, worldwide, royalty-free license under its licensed
// patents to make, have made, use, sell, offer for sale, import, and/or
// otherwise dispose of its contribution in the software or derivative works
// of the contribution in the software.
// 
// 3. Conditions and Limitations
// 
// (A) No Trademark License- This license does not grant you rights to use
// any contributors' name, logo, or trademarks.
// 
// (B) If you bring a patent claim against any contributor over patents that
// you claim are infringed by the software, your patent license from such
// contributor to the software ends automatically.
// 
// (C) If you distribute any portion of the software, you must retain all
// copyright, patent, trademark, and attribution notices that are present
// in the software.
// 
// (D) If you distribute any portion of the software in source code form,
// you may do so only under this license by including a complete copy of
// this license with your distribution. If you distribute any portion of the
// software in compiled or object code form, you may only do so under a
// license that complies with this license.
// 
// (E) The software is licensed "as-is." You bear the risk of using it. The
// contributors give no express warranties, guarantees or conditions. You
// may have additional consumer rights under your local laws which this
// license cannot change. To the extent permitted under your local laws,
// the contributors exclude the implied warranties of merchantability,
// fitness for a particular purpose and non-infringement.
// 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BeerMath
{
	/// <summary>
	/// Helper class for dealing with common malt calculations 
	/// </summary>
    public sealed class Malt
	{
		#region Gravity Constants
		public const decimal RealExtractFinalGravityRatio		= 0.8192m;
		public const decimal RealExtractOriginalGravityRatio	= 0.1808m;
		#endregion

		/// <summary>
		///  Caculates color in MCU (Malt Color Units) 
		/// </summary>
		/// <param name="GrainLbs">
		/// A <see cref="System.Decimal"/> representing pounds of grain
		/// </param>
		/// <param name="DegreesLovibond">
		/// A <see cref="System.Decimal"/> representing degrees Lovibond of the grain
		/// </param>
		/// <param name="VolumeGallons">
		/// A <see cref="System.Decimal"/> representing the volume of wort made with this grain charge
		/// </param>
		/// <returns>
		/// A <see cref="BeerColor"/>
		/// </returns>
        public static BeerColor CalculateMcu(decimal GrainLbs, decimal DegreesLovibond, decimal VolumeGallons)
        {
            return new BeerColor((GrainLbs * DegreesLovibond) / VolumeGallons, BeerColorType.Mcu);
        }
		
		/// <summary>
		///  Calculates color using SRM (Standard Reference Method) 
		/// </summary>
		/// <param name="GrainLbs">
		/// A <see cref="System.Decimal"/> representing pounds of grain
		/// </param>
		/// <param name="DegreesLovibond">
		/// A <see cref="System.Decimal"/> representing degrees Lovibond of the grain
		/// </param>
		/// <param name="VolumeGallons">
		/// A <see cref="System.Decimal"/> representing the volume of wort made with this grain charge
		/// </param>
		/// <returns>
		/// A <see cref="BeerColor"/>
		/// </returns>
	    public static BeerColor CalculateSrm(decimal GrainLbs, decimal DegreesLovibond, decimal VolumeGallons)
        {
			BeerColor Mcu = new BeerColor((GrainLbs * DegreesLovibond) / VolumeGallons, BeerColorType.Mcu);
			return new BeerColor(Mcu.Srm, BeerColorType.Srm);
        }
		
		/// <summary>
		///  Calculates color using EBC (European Brewing Convention) 
		/// </summary>
		/// <param name="GrainLbs">
		/// A <see cref="System.Decimal"/> representing pounds of grain
		/// </param>
		/// <param name="DegreesLovibond">
		/// A <see cref="System.Decimal"/> representing degrees Lovibond of the grain
		/// </param>
		/// <param name="VolumeGallons">
		/// A <see cref="System.Decimal"/> representing the volume of wort made with this grain charge
		/// </param>
		/// <returns>
		/// A <see cref="BeerColor"/>
		/// </returns>
		public static BeerColor CalculateEbc(decimal GrainLbs, decimal DegreesLovibond, decimal VolumeGallons)
		{
			BeerColor Mcu = new BeerColor((GrainLbs * DegreesLovibond) / VolumeGallons, BeerColorType.Mcu);
			return new BeerColor(Mcu.Ebc, BeerColorType.Ebc);
		}

		/// <summary>
		/// Calculates the gravity contribution of a fermentable.
		/// </summary>
		/// <param name="GrainLbs">
		/// A <see cref="System.Decimal"/> representing the pounds of grain.
		/// </param>
		/// <param name="ExtractPpg">
		/// A <see cref="System.Decimal"/> representing the extract PPG rating for the grain.
		/// </param>
		/// <param name="ExtractEfficiency">
		/// A <see cref="System.Decimal"/> representing the estimated extract efficiency.
		/// </param>
		/// <param name="Volume">
		/// A <see cref="System.Decimal"/> representing the total volume of the batch in gallons.
		/// </param>
		/// <returns>
		/// The <see cref="Gravity"/> contribution of the grain.
		/// </returns>
		public static Gravity CalculateOriginalGravity(decimal GrainLbs, decimal ExtractPpg, decimal ExtractEfficiency, decimal Volume)
		{
			if (Volume == 0m)
			{
				throw new BeerMathException("volume cannot be 0.");
			}
			return new Gravity((GrainLbs * ExtractPpg * ExtractEfficiency) / Volume);
		}

		/// <summary>
		/// Calculates the final gravity of a fermentable.
		/// </summary>
		/// <param name="GrainLbs">
		/// A <see cref="System.Decimal"/> representing the pounds of grain.
		/// </param>
		/// <param name="ExtractPpg">
		/// A <see cref="System.Decimal"/> representing the extract PPG rating for the grain.
		/// </param>
		/// <param name="ExtractEfficiency">
		/// A <see cref="System.Decimal"/> representing the estimated extract efficiency.
		/// </param>
		/// <param name="Volume">
		/// A <see cref="System.Decimal"/> representing the total volume of the batch in gallons.
		/// </param>
		/// <param name="ApparentAttenuation">
		/// A <see cref="System.Decimal"/> representing the apparent attenuation.
		/// </param>
		/// <returns>
		/// The final <see cref="Gravity"/> contribution of the grain.
		/// </returns>
		public static Gravity CalculateFinalGravity(decimal GrainLbs, decimal ExtractPPG, decimal ExtractEfficiency,
			decimal Volume, decimal ApparentAttenuation)
		{
			return CalculateFinalGravity(CalculateOriginalGravity(GrainLbs, ExtractPPG, ExtractEfficiency, Volume),
				ApparentAttenuation);
		}

		/// <summary>
		/// Calculates the final gravity of a fermentable.
		/// </summary>
		/// <param name="OriginalGravity">
		/// The original <see cref="Gravity"/> contribution of the grain.
		/// </param>
		/// <param name="ApparentAttenuation">
		/// A <see cref="System.Decimal"/> representing the apparent attenuation.
		/// </param>
		/// <returns>
		/// The final <see cref="Gravity"/> contribution of the grain.
		/// </returns>
		public static Gravity CalculateFinalGravity(Gravity OriginalGravity, decimal ApparentAttenuation)
		{
			return new Gravity(OriginalGravity.Points * (1.0m - ApparentAttenuation));
		}

		/// <summary>
		/// Calculates the Real Extract Gravity value for the grain.
		/// </summary>
		/// <param name="OriginalGravity">
		/// The original <see cref="Gravity"/> contribution of the grain.
		/// </param>
		/// <param name="FinalGravity">
		/// The final <see cref="Gravity"/> contribution of the grain.
		/// </param>
		/// <returns>
		/// The <see cref="Gravity"/> Real Extract value.
		/// </returns>
		public static Gravity CalculateRealExtract(Gravity OriginalGravity, Gravity FinalGravity)
		{
			decimal realExtractPlato = (RealExtractFinalGravityRatio * FinalGravity.Plato) + (RealExtractOriginalGravityRatio * OriginalGravity.Plato);
			decimal realExtractPointsValue = Gravity.ConvertPlatoToPoints(realExtractPlato);
			return new Gravity(realExtractPointsValue);
		}
	}
}
